package cn.eeepay.boss.system;

import cn.eeepay.framework.db.pagination.Page;
import cn.eeepay.framework.model.BossOperLog;
import cn.eeepay.framework.model.Result;
import cn.eeepay.framework.model.UserLoginInfo;
import cn.eeepay.framework.service.BossOperLogService;
import cn.eeepay.framework.util.CommonUtil;
import cn.eeepay.framework.util.Constants;
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
    
/**
 * AOP日志记录切面
 * @author YeXiaoMing
 * @date 2016年12月13日上午11:37:42
 */
@Aspect    
@Component
@Order(1)
public  class SystemLogAspect { 
	
	private final Logger log = LoggerFactory.getLogger(SystemLogAspect.class);

	private ThreadLocal<Integer> threadLocalOperId = new ThreadLocal<>();//操作ID

	public void setThreadLocalOperId(Integer operId) {
		threadLocalOperId.set(operId);
	}

	@Autowired
	private BossOperLogService bossOperLogService;
	
	/**
	 * 切入点
	 */
    @Pointcut("@annotation(cn.eeepay.boss.system.SystemLog)")
    public void controllerAspect() {
    }

    @DataSource
    @Before("controllerAspect()")
	public void insertLog(JoinPoint joinPoint){
		try {
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			//记录日志的传参
			BossOperLog bossOperLog = new BossOperLog();
			String[] op = getControllerMethodDescription(joinPoint);
			bossOperLog.setMethod_desc(op[0]);//方法描述
			String operCode = op[1];
			if (StringUtils.isBlank(operCode)) {
				//当获取不到操作代码时，自动获取接口地址当作操作代码
				operCode = convertRequestUrl(request);
			}
			bossOperLog.setOper_code(operCode);//菜单代码
			bossOperLog.setOper_ip(getRemoteAddr(request));//请求IP
			bossOperLog.setRequest_method(op[2]);//请求方法
			bossOperLog.setRequest_params(getRequestParams(joinPoint));//请求参数

			UserLoginInfo loginUser = CommonUtil.getLoginUser();
			bossOperLog.setUser_id(loginUser.getId());//用户ID
			bossOperLog.setUser_name(loginUser.getRealName());//用户姓名
			//保存数据库
			bossOperLogService.insert(bossOperLog);
			setThreadLocalOperId(bossOperLog.getId());
		}  catch (Exception e) {
			log.error("记录操作日志失败", e);
		}
	}

	/**
	 * 将请求路径转换为字符串
	 * @param request
	 * @return
	 */
	private String convertRequestUrl(HttpServletRequest request) {
		String operCode;
		String ctx = request.getContextPath();
		String uri = request.getRequestURI().replace(ctx, "");
		StringBuffer sb = new StringBuffer();
		if (uri.indexOf("/") > -1) {
			String[] names = uri.split("/");
			for (String name : names) {
				if (StringUtils.isNotBlank(name)) {
					sb.append(name).append(".");
				}
			}
			sb.setLength(sb.length() - 1);
		}
		operCode = sb.toString();
		return operCode;
	}

	/**
	 * 配置后置返回通知
	 * @param result
	 */
	@SuppressWarnings("unchecked")
	@DataSource
	@AfterReturning(pointcut="controllerAspect()",returning="result")
	public void afterReturn(Object result){
        try {
        	if(threadLocalOperId.get() == null) {
        		return;
			}
			Map<String, Object> result_map= null;
			if (result instanceof  Map){
				result_map = (Map<String, Object>)result;
			}else if(result instanceof Result){
				Result temp = (Result) result;
				result_map = new HashMap<>();
				result_map.put("status", temp.isStatus());
			}else if(result instanceof Page){
				return;
			}
            BossOperLog  bossOperLog =new BossOperLog();
			bossOperLog.setId(threadLocalOperId.get());
            //返回状态参数不统一(有的是result，有的是bols, status)
			if(result_map!=null && result_map.get("result")!=null){
				if((Boolean)result_map.get("result")){
					bossOperLog.setOper_status(CommonConst.ONE);//成功
				}else{
					bossOperLog.setOper_status(CommonConst.ZERO);//失败
				}
			}
			if(result_map!=null && result_map.get("bols")!=null){
				if((Boolean)result_map.get("bols")){
					bossOperLog.setOper_status(CommonConst.ONE);//成功
				}else{
					bossOperLog.setOper_status(CommonConst.ZERO);//失败
				}
			}
			if(result_map!=null && result_map.get("status")!=null){
				if((Boolean)result_map.get("status")){
					bossOperLog.setOper_status(CommonConst.ONE);//成功
				}else{
					bossOperLog.setOper_status(CommonConst.ZERO);//失败
				}
			}
			if(result_map!=null && result_map.get("state")!=null){
				if((Boolean)result_map.get("state")){
					bossOperLog.setOper_status(CommonConst.ONE);//成功
				}else{
					bossOperLog.setOper_status(CommonConst.ZERO);//失败
				}
			}
            String result_str=JSON.toJSONString(result);
            if(result_str.length()>100){//有的结果会返回异常
            	result_str=result_str.substring(0, 99);
            }
            bossOperLog.setReturn_result(result_str);//返回结果
            //更新操作结果
            bossOperLogService.update(bossOperLog);
			threadLocalOperId.remove();
        }  catch (Exception e) {
            //记录本地异常日志    
        	log.error("更新操作日志结果异常", e);
        }
	}

	@DataSource
	@AfterThrowing(throwing = "ex", pointcut = "controllerAspect()")
	public void logException(Throwable ex){
		try{
			if(threadLocalOperId.get() == null) {
				return;
			}
			BossOperLog  bossOperLog =new BossOperLog();
			bossOperLog.setId(threadLocalOperId.get());
			bossOperLog.setOper_status(CommonConst.ZERO);//失败
			bossOperLog.setReturn_result(ex.toString());//异常信息
			bossOperLogService.update(bossOperLog);
			threadLocalOperId.remove();
		} catch (Exception e) {
			log.error("更新日志异常", e);
		}
	}

	/**
	 * 获取注解中对方法的描述信息
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 */
     public  String[] getControllerMethodDescription(JoinPoint joinPoint)  throws Exception {
		 MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		 String description = signature.getMethod().getAnnotation(SystemLog.class).description();
		 String operCode = signature.getMethod().getAnnotation(SystemLog.class).operCode();
		 String longName = signature.toLongString();
		 return new String[]{description, operCode, longName};
	 }
     
	/**
	 * 获取请求参数
	 * @param joinPoint
	 * @return
	 * @throws Exception
	 */
      public  String getRequestParams(JoinPoint joinPoint)  throws Exception {    
          StringBuilder params = new StringBuilder();    
          if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {    
              for ( int i = 0; i < joinPoint.getArgs().length; i++) {
				try {
					Object obj=joinPoint.getArgs()[i];
					if(obj instanceof String )
						params.append(obj.toString()).append(",");
					else
						params.append(JSON.toJSONString(obj)).append(",");
				}catch (Exception e) {
				}
             }    
         }
         //去除结尾的;
         int index_od = params.lastIndexOf(";");
         if(index_od != -1 && index_od == (params.length()-1)){
         	params.setLength(params.length()-1);
         }
//         大于10000且小于20000的打印出来，并截取10000存入数据库
//         超过20000的不打印（一般就是导入）
         if(params.length()>10000){ //导入文件参数过长
//        	 params="导入文件参数过长";
//        	 log.info("导入参数过长，原参数：{}", params);

			 //导入操作,无用日志，去除掉
        	 params.setLength(1000);
         }
         return params.toString();
     } 
     
	/**
	 * 返回客户端IP地址
	 * @param request
	 * @return
	 */
 	protected String getIpAddr(HttpServletRequest request) {
 		String ip = request.getHeader("X-Forwarded-For");
 		if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
 			// 多次反向代理后会有多个ip值，第一个ip才是真实ip
 			int index = ip.indexOf(",");
 			if (index != -1) {
 				return ip.substring(0, index);
 			} else {
 				return ip;
 			}
 		}
 		ip = request.getHeader("X-Real-IP");
 		if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
 			return ip;
 		}
 		return request.getRemoteAddr();
 	}

	private String getRemoteAddr(HttpServletRequest request){
		try {
			String xforwardedFor = request.getHeader("x-forwarded-for");
			String xRealIp = request.getHeader("X-Real-IP");
//			log.info("请求头数据: key = x-forwarded-for, value = {}", xforwardedFor);
//			log.info("请求头数据: key = X-Real-IP, value = {}", xRealIp);
			if (StringUtils.isNotBlank(xforwardedFor)) {
				return xforwardedFor.split(",")[0];
			}
			if (StringUtils.isNotBlank(xRealIp)) {
				return xRealIp;
			}
			return request.getRemoteAddr();
		}catch (Exception e){
			log.error("getRemoteAddr ==> " + e);
			return "";
		}
	}
}
